/*
 * Copyright 2012 Marsrutai.lt
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package lt.marsrutai.mobile.android.service.impl;

import lt.marsrutai.mobile.android.service.ShapeManipulationService;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;

/**
 * <p>Default implementation</p>
 * 
 * @author Justas
 */
public class ShapeManipulationServiceImpl implements ShapeManipulationService {

    @Override
    public Bitmap combineShapes(Context context, int shape1Id, int shape2Id) {
        Resources resources = context.getResources();
        
        Bitmap shape1Bitmap = BitmapFactory.decodeResource(resources, shape1Id);
        Bitmap shape2Bitmap = BitmapFactory.decodeResource(resources, shape2Id);
        
        return combineImages(shape1Bitmap, shape2Bitmap);
    }
    
    @Override
    public Bitmap combineShapes(Drawable shape1, Drawable shape2) {
        Bitmap shape1Bitmap = drawableToBitmap(shape1);
        Bitmap shape2Bitmap = drawableToBitmap(shape2);
        
        return combineImages(shape1Bitmap, shape2Bitmap);
    }
    
    @Override
    public Bitmap combineShapes(Bitmap shape1, Bitmap shape2) {
        return combineImages(shape1, shape2);
    }

    public Bitmap drawableToBitmap(Drawable drawable) {
        if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable) drawable).getBitmap();
        }

        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);

        return bitmap;
    }

    @Override
    public Drawable bitmapToDrawable(Context context, Bitmap bitmap) {
        Resources resources = context.getResources();
        return new BitmapDrawable(resources, bitmap);
    }

    @Override
    public Drawable colorShape(Context context, Drawable drawable, int targetColor) {
        Resources resources = context.getResources();
        
        return new BitmapDrawable(resources, colorShape(drawableToBitmap(drawable), targetColor));
    }

    @Override
    public Drawable colorShape(Context context, int shapeId, int targetColor) {
        Resources resources = context.getResources();
        
        return colorShape(context, resources.getDrawable(shapeId), targetColor);
    }

    @Override
    public Bitmap rotateShape(Context context, Bitmap image, float degrees) {
        Matrix matrix = new Matrix();
        matrix.postRotate(degrees);

        Bitmap bitmap = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(), matrix, true);
        
        return bitmap;
    }

    /**
     * <p>
     * Color a provided shape with a solid color.
     * </p>
     * <p>
     * All pixels will have their colors changed, while still preserving their
     * transparency.
     * </p>
     * 
     * @param shape
     *            Shape which will have it's color changed
     * @param newShapeColor
     *            New shape color (transparency byte will be ignored)
     */
    private Bitmap colorShape(Bitmap shape, int newShapeColor) {
        // Create a mutable bitmap from the original
        shape = shape.copy(shape.getConfig(), true);
        
        for (int y = 0; y < shape.getHeight(); y++) {
            for (int x = 0; x < shape.getWidth(); x++) {
                // Extract shape's transparency byte
                int transparency = shape.getPixel(x, y) & 0xFF000000;
                
                // Keep each pixel's transparency, but change their color to newShapeColor
                int targetColor = newShapeColor & 0x00FFFFFF; 
                shape.setPixel(x, y, transparency | targetColor);
            }
        }
        return shape;
    }

    /**
     * <p>
     * Combine two {@link Bitmap}s. Centers the second shape.
     * </p>
     * 
     * @param shape1
     * @param shape2
     * @return
     */
    private Bitmap combineImages(Bitmap shape1, Bitmap shape2) {
        Bitmap combinedImage = null;

        int shapeWidth = Math.max(shape1.getWidth(), shape2.getWidth());
        int shapeHeight = Math.max(shape1.getHeight(), shape2.getHeight());
        combinedImage = Bitmap.createBitmap(shapeWidth, shapeHeight, Bitmap.Config.ARGB_8888);

        Canvas comboImage = new Canvas(combinedImage);

        // center shape1
        int shape1Width = shape1.getWidth();
        int shape1Height = shape1.getHeight();

        float shape1Left = (float) (shapeWidth - shape1Width) / 2;
        float shape1Top = (float) (shapeHeight - shape1Height) / 2;
        
        // center shape2
        int shape2Width = shape2.getWidth();
        int shape2Height = shape2.getHeight();

        float shape2Left = (float) (shapeWidth - shape2Width) / 2;
        float shape2Top = (float) (shapeHeight - shape2Height) / 2;

        // Put both shape and it's overlay on top of each other
        comboImage.drawBitmap(shape1, shape1Left, shape1Top, null);
        comboImage.drawBitmap(shape2, shape2Left, shape2Top, null);

        return combinedImage;
    }
}
